package com.uinnova.product.eam.service.impl;

import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.comm.model.es.*;
import com.uinnova.product.eam.constant.CheckoutType;
import com.uinnova.product.eam.dto.*;
import com.uinnova.product.eam.model.EamArtifactVo;
import com.uinnova.product.eam.model.cj.domain.DeliverableTemplate;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.cj.vo.BindAssetVo;
import com.uinnova.product.eam.model.enums.AssetType;
import com.uinnova.product.eam.model.enums.CategoryTypeEnum;
import com.uinnova.product.eam.service.*;
import com.uinnova.product.eam.service.cj.dao.DeliverableTemplateDao;
import com.uinnova.product.eam.service.cj.service.PlanDesignInstanceService;
import com.uinnova.product.eam.service.es.EamCategoryDesignDao;
import com.uinnova.product.eam.service.es.EamCategoryPrivateDao;
import com.uinnova.product.eam.service.fx.GeneralPullSvc;
import com.uinnova.product.eam.vo.*;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.project.base.diagram.util.RedisUtil;
import com.uino.api.client.permission.IRoleApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.permission.base.SysRole;
import com.uino.bean.permission.base.SysUser;
import com.uino.bean.permission.business.UserInfo;
import com.uino.bean.permission.query.CSysUser;
import com.uino.bean.permission.query.UserSearchBeanExtend;
import com.uino.dao.util.ESUtil;
import com.uino.service.cmdb.microservice.impl.CIClassSvc;
import com.uino.service.permission.microservice.IUserSvc;
import com.uino.util.sys.SysUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
@Log4j2
public class CyAutoCreateServiceImpl implements CyAutoCreateService {

    private static final String DEFAULT_CREATOR = "admin";
    private static final Long DEFAULT_DOMAIN_ID = 1L;
    @Autowired
    private EamCategoryPrivateDao categoryPrivateDao;
    @Autowired
    private EamCategoryDesignDao categoryDesignDao;
    @Autowired
    private IUserSvc userSvc;
    @Autowired
    private IRoleApiSvc roleApiSvc;
    @Autowired
    private IFolderPermissionManagerService folderPermissionManagerService;
    @Autowired
    private CIClassSvc ciClassSvc;
    @Autowired
    private IamsCISwitchSvc ciSwitchSvc;
    @Autowired
    private AXEAConfService axeaConfService;
    @Autowired
    private IEamArtifactSvc artifactSvc;
    @Autowired
    private EamCategorySvc eamCategorySvc;
    @Autowired
    private GeneralPullSvc generalPullSvc;
    @Autowired
    private EamDiagramRelationSysService diagramRelationSysService;
    @Autowired
    private PlanDesignInstanceService planDesignInstanceService;
    @Autowired
    private DeliverableTemplateDao deliverableTemplateDao;
    @Autowired
    private RedisUtil redisUtil;
    @Value("${http.resource.space}")
    private String httpPath;


    @Override
    public void categoryAutoCreate() {
        String key = "lock:category:auto:create:" + httpPath.replace(":", "");
        boolean lock = redisUtil.setnx(key, "1", 60, TimeUnit.SECONDS);
        if (!lock) {
            log.info("获取锁失败");
            return;
        }
        try {
            doCategoryAutoCreate();
        } catch (Exception e) {
            log.error("创建系统目录失败:{}",JSON.toJSONString(e));
        }
        redisUtil.del(key);
    }

    @Async
    private void doCategoryAutoCreate() {
        AXEAConf conf = axeaConfService.getCategoryAutoConf();
        List<String> categoryCreateClassCodes = conf.getCategoryCreates();
        for (String categoryCreateClassCode : categoryCreateClassCodes) {
            List<CcCiInfo> ciInfos = getCIByClassCode(categoryCreateClassCode);
            if (CollectionUtils.isEmpty(ciInfos)) {
                log.error("分类下无数据:ciClassCode:{}", categoryCreateClassCode);
                continue;
            }
            // 标注下 如果是项目资产 涉及归档
            boolean archiveFlag = false; // 是否需要归档
            String projectClassCode = conf.getClassMapping().getProjectClassCode();
            if (projectClassCode.equals(categoryCreateClassCode)) {
                archiveFlag = true;
            }
            AXEAClassDetailConf classDetailConf = conf.getClassConf().get(categoryCreateClassCode);
            List<String> ownerDefs = classDetailConf.getOwnerDefs();
            List<CyAutoCreateDTO> list = new ArrayList<>();
            for (CcCiInfo ciInfo : ciInfos) {
                List<String> owners = new ArrayList<>();
                for (String ownerDef : ownerDefs) {
                    String owner = ciInfo.getAttrs().get(ownerDef);
                    List<String> loginCodes = this.analysisLoginCode(owner);
                    if (!CollectionUtils.isEmpty(loginCodes)) {
                        owners.addAll(loginCodes);
                    }
                }
                String label = getLabel(ciInfo.getCi());
                if (CollectionUtils.isEmpty(owners) || StringUtils.isBlank(label)) {
                    continue;
                }
                CyAutoCreateDTO autoCreateVo = new CyAutoCreateDTO();
                autoCreateVo.setClassCode(categoryCreateClassCode);
                autoCreateVo.setLabel(label);
                autoCreateVo.setCiCode(ciInfo.getCi().getCiCode());
                autoCreateVo.setOwners(owners.stream().distinct().collect(Collectors.toList()));
                autoCreateVo.setCreateTime(ciInfo.getCi().getCreateTime());
                list.add(autoCreateVo);
            }
            this.categoryCreate(list, classDetailConf, categoryCreateClassCode, archiveFlag);
        }
    }

    private String getLabel(CcCi ci) {
        String ciLabel = ci.getCiLabel();
        if (StringUtils.isBlank(ciLabel)) {
            return null;
        }
        List<String> datas = JSONObject.parseArray(ciLabel, String.class);
        if (CollectionUtils.isEmpty(datas)) {
            return null;
        }
        return String.join("-", datas);
    }

    private void categoryCreate(List<CyAutoCreateDTO> list, AXEAClassDetailConf classDetailConf, String categoryCreateClassCode, Boolean archiveFlag) {
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        //准备创建系统文件夹上下文
        CyAutoCreateContext context = prepareContext(list, classDetailConf, categoryCreateClassCode, archiveFlag);
        //私有库系统文件夹自动创建
        privateSysCategoryAutoCreate(context);
        //资产库系统文件夹自动创建
        designSysCategoryAutoCreate(context);
    }

    private List<String> analysisLoginCode(String data) {
        if (StringUtils.isBlank(data) || data.equals("null")) {
            return new ArrayList<>();
        }
        try {
            List<SysUser> users = JSON.parseArray(data, SysUser.class);
            return users.stream().map(SysUser::getLoginCode).collect(Collectors.toList());
        } catch (Exception e) {
            log.error("人员数据解析异常:{}", data);
        }
        return new ArrayList<>();
    }

    /**
     * 创建系统文件夹上下文
     * @param list
     * @param classDetailConf
     * @param categoryCreateClassCode
     * @return
     */
    private CyAutoCreateContext prepareContext(List<CyAutoCreateDTO> list, AXEAClassDetailConf classDetailConf, String categoryCreateClassCode, Boolean archiveFlag) {
        //负责人
        List<String> owners = new ArrayList<>();
        list.stream().map(CyAutoCreateDTO::getOwners).forEach(owners::addAll);
        owners = owners.stream().filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList());
        //负责人我的空间系统文件夹
        List<EamCategory> privateSysCategories = queryPrivateSysList(owners);
        //资产库owners下当前分类ci的系统文件夹
        List<EamCategory> designSysCategories = queryDesignSysList(classDetailConf.getDesignRootId());
        //负责人详细信息
        Map<String, SysUser> userMap = this.getUserMap(owners);
        //负责人"我的空间"当前分类的普通文件夹
        List<EamCategory> privateNorCategories = queryPrivateAppProductNor(owners, categoryCreateClassCode);
        return CyAutoCreateContext.builder()
                .autoCreateList(list)
                .owners(owners)
                .userMap(userMap)
                .privateSysCategories(privateSysCategories)
                .designSysCategories(designSysCategories)
                .privateNorCategories(privateNorCategories)
                .categoryCreateClassCode(categoryCreateClassCode)
                .designRootId(classDetailConf.getDesignRootId())
                .designFolderAuthRoleNames(classDetailConf.getDesignFolderAuthRoleNames())
                .archiveFlag(archiveFlag)
                .build();
    }

    /**
     * 获取负责人"我的空间"系统文件夹
     * @param owners
     * @return
     */
    private List<EamCategory> queryPrivateSysList(List<String> owners) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.SYSTEM.val()));
        query.must(QueryBuilders.termsQuery("ownerCode.keyword", owners));
        return categoryPrivateDao.getListByQueryScroll(query);
    }

    /**
     * 获取负责人"我的空间"当前分类的普通文件夹
     * @param owners
     * @param categoryCreateClassCode
     * @return
     */
    private List<EamCategory> queryPrivateAppProductNor(List<String> owners, String categoryCreateClassCode) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.UNIVERSAL.val()));
        query.must(QueryBuilders.termsQuery("ownerCode.keyword", owners));
        query.must(QueryBuilders.termQuery("cyAutoCreateType.keyword", categoryCreateClassCode));
        return categoryPrivateDao.getListByQueryScroll(query);
    }

    private List<CcCiInfo> getCIByClassCode(String ciClassCode) {
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassCodeEqual(ciClassCode);
        List<CcCiClassInfo> classInfos = ciClassSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(classInfos)) {
            return new ArrayList<>();
        }
        CcCiClassInfo ciClassInfo = classInfos.get(0);
        CCcCi ciCdt = new CCcCi();
        ciCdt.setClassId(ciClassInfo.getCiClass().getId());
        List<CcCiInfo> ciInfos = new ArrayList<>();
        Page<CcCiInfo> page = ciSwitchSvc.queryCiInfoPage(DEFAULT_DOMAIN_ID, 1, 2000, ciCdt, "id", true, false, LibType.DESIGN);
        if (!CollectionUtils.isEmpty(page.getData())) {
            ciInfos.addAll(page.getData());
        }
        for (int pageNum = 2; pageNum <= page.getTotalPages(); pageNum++) {
            page = ciSwitchSvc.queryCiInfoPage(DEFAULT_DOMAIN_ID, 1, 2000, ciCdt, "id", true, false, LibType.DESIGN);
            ciInfos.addAll(page.getData());
        }
        return ciInfos;
    }

    private List<CcCiInfo> getCIByYear(String ciClassCode, List<String> ciCodes, String year) {
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassCodeEqual(ciClassCode);
        List<CcCiClassInfo> classInfos = ciClassSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(classInfos)) {
            return new ArrayList<>();
        }
        CcCiClassInfo ciClassInfo = classInfos.get(0);
        CCcCi ciCdt = new CCcCi();
        ciCdt.setClassId(ciClassInfo.getCiClass().getId());
        if (!BinaryUtils.isEmpty(ciCodes)) {
            ciCdt.setCiCodes(ciCodes.toArray(new String[0]));
        }
        if (!BinaryUtils.isEmpty(year)) {
            ciCdt.setStartCreateTime(Long.valueOf(year + "0101000000"));
            ciCdt.setEndCreateTime(Long.valueOf(year + "1231235959"));
        }
        List<CcCiInfo> ciInfos = new ArrayList<>();
        Page<CcCiInfo> page = ciSwitchSvc.queryCiInfoPage(DEFAULT_DOMAIN_ID, 1, 2000, ciCdt, "id", true, false, LibType.DESIGN);
        if (!CollectionUtils.isEmpty(page.getData())) {
            ciInfos.addAll(page.getData());
        }
        for (int pageNum = 2; pageNum <= page.getTotalPages(); pageNum++) {
            page = ciSwitchSvc.queryCiInfoPage(DEFAULT_DOMAIN_ID, 1, 2000, ciCdt, "id", true, false, LibType.DESIGN);
            ciInfos.addAll(page.getData());
        }
        return ciInfos;
    }

    private CcCiInfo getCIByCiCode(String ciCode) {
        CCcCi ciCdt = new CCcCi();
        ciCdt.setCiCodeEqual(ciCode);
        Page<CcCiInfo> page = ciSwitchSvc.queryCiInfoPage(DEFAULT_DOMAIN_ID, 1, 1, ciCdt, "id", true, true, LibType.DESIGN);
        return CollectionUtils.isEmpty(page.getData()) ? null : page.getData().get(0);
    }

    /**
     * 获取当前分类资产库下的系统文件夹
     * @param designRootId
     * @return
     */
    private List<EamCategory> queryDesignSysList(Long designRootId) {
        // 查询根目录下系统文件夹及归档文件夹下的系统文件夹
        List<Long> parentIds = Collections.singletonList(designRootId);
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.ARCHIVE.val()));
        query.must(QueryBuilders.termQuery("dataStatus", 1));
        query.must(QueryBuilders.termQuery("parentId", designRootId));
        List<EamCategory> archiveCategoryList = categoryDesignDao.getListByQueryScroll(query);
        if (!BinaryUtils.isEmpty(archiveCategoryList)) {
            parentIds.addAll(archiveCategoryList.stream().map(EamCategory::getId).collect(Collectors.toList()));
        }
        query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.SYSTEM.val()));
        query.must(QueryBuilders.termQuery("dataStatus", 1));
        query.must(QueryBuilders.termsQuery("parentId", parentIds));
        List<EamCategory> result = categoryDesignDao.getListByQueryScroll(query);
        if (result != null) {
            result.forEach(each -> each.setRootParentId(designRootId));
        }
        return result;
    }

    private Map<String, SysUser> getUserMap(List<String> owners) {
        if (CollectionUtils.isEmpty(owners)) {
            return new ConcurrentHashMap<>();
        }
        CSysUser cSysUser = new CSysUser();
        cSysUser.setLoginCodes(owners.toArray(new String[owners.size()]));
        List<SysUser> userInfos = userSvc.getSysUserByCdt(cSysUser);
        return userInfos.stream().collect(Collectors.toMap(SysUser::getLoginCode, e -> e));
    }

    /**
     * 私有库系统文件夹自动创建
     * @param context
     */
    private void privateSysCategoryAutoCreate(CyAutoCreateContext context) {
        //私有库已有系统文件夹
        List<EamCategory> categories = context.getPrivateSysCategories();
        //负责人分组
        Map<String, List<EamCategory>> cyGroupByOwner = categories
                .stream().collect(Collectors.groupingBy(EamCategory::getOwnerCode));
        //一个分类ci对应多个负责人 转换为 一个负责人对应多个分类ci
        Map<String, List<CyAutoCreateDTO>> ownerRelateMap = new ConcurrentHashMap<>();
        for (CyAutoCreateDTO autoCreateDTO : context.getAutoCreateList()) {
            //过滤掉系统内不存在的负责人
            Set<String> owners = autoCreateDTO.getOwners().stream().filter(owner ->
                    context.getUserMap().containsKey(owner)).collect(Collectors.toSet());
            if (CollectionUtils.isEmpty(owners)) {
                continue;
            }
            for (String owner : owners) {
                List<CyAutoCreateDTO> ownerRelates = ownerRelateMap.getOrDefault(owner, new ArrayList<>());
                ownerRelates.add(autoCreateDTO);
                ownerRelateMap.put(owner, ownerRelates);
            }
        }
        //创建负责人下没有的系统文件夹
        List<EamCategory> insertCategories = new ArrayList<>();
        //若分类ci的label发生变更，更新系统文件夹名称
        List<EamCategory> updateCategories = new ArrayList<>();
        for (Map.Entry<String, List<CyAutoCreateDTO>> entry : ownerRelateMap.entrySet()) {
            String owner = entry.getKey();
            //负责人下如果不存在当前分类的普通文件夹就创建--回收站的也算存在
            EamCategory norCategory = createNorIfNotPresent(context, owner, insertCategories);
            //负责人已有系统文件夹
            Map<String, EamCategory> exists = cyGroupByOwner.getOrDefault(owner, new ArrayList<>())
                    .stream().collect(Collectors.toMap(EamCategory::getCiCode, e -> e, (k1,k2) -> k2));
            for (CyAutoCreateDTO autoCreateVo : entry.getValue()) {
                EamCategory existCategory = exists.get(autoCreateVo.getCiCode());
                if (existCategory == null) {
                    addSysCategory(autoCreateVo, owner, norCategory, insertCategories);
                    continue;
                }
                if (existCategory.getDataStatus() != null
                        && existCategory.getDataStatus() == 1) {
                    updateCategoryIfNeed(existCategory, autoCreateVo, updateCategories);
                }
            }
        }
        if (!CollectionUtils.isEmpty(insertCategories)) {
            categoryPrivateDao.saveOrUpdateBatch(insertCategories);
        }
        if (!CollectionUtils.isEmpty(updateCategories)) {
            categoryPrivateDao.saveOrUpdateBatch(updateCategories);
        }
    }

    /**
     * 若应用系统or产品名称发生变更，更新系统文件夹名称
     * @param existCategory
     * @param autoCreateVo
     * @param updateCategories
     */
    private void updateCategoryIfNeed(EamCategory existCategory, CyAutoCreateDTO autoCreateVo, List<EamCategory> updateCategories) {
        String newDirName = autoCreateVo.getLabel();
        if (existCategory.getDirName().equals(newDirName)) {
            return;
        }
        existCategory.setDirName(newDirName);
        updateCategories.add(existCategory);
    }

    /**
     * 如果不存在当前分类的普通文件夹就创建
     * @apiNote 如果当前分类ci的文件在回收站里，那么新增的系统文件夹丢到根目录
     * @param context
     * @param owner
     * @param insertCategories
     * @return
     */
    private EamCategory createNorIfNotPresent(CyAutoCreateContext context,
                                                           String owner, List<EamCategory> insertCategories) {
        List<EamCategory> privateNorCategories = context.getPrivateNorCategories();
        //应用系统or产品在回收站，系统文件夹丢到根目录
        EamCategory privateRootCategory = new EamCategory();
        privateRootCategory.setId(0L);
        //需求：私有库系统文件夹丢根目录
        return privateRootCategory;
        //if (!CollectionUtils.isEmpty(privateNorCategories)
        //        && privateNorCategories.get(0).getDataStatus() == 0) {
        //    return privateRootCategory;
        //}
        //if (!CollectionUtils.isEmpty(privateNorCategories)) {
        //    return privateNorCategories.get(0);
        //}
        //return addPrivateNorCategory(context.getCategoryCreateClassCode(), owner, insertCategories);
    }

    /**
     * 加入系统文件夹
     * @param createVo
     * @param owner
     * @param parentCategory
     * @param insertCategories
     */
    private void addSysCategory(CyAutoCreateDTO createVo, String owner, EamCategory parentCategory, List<EamCategory> insertCategories) {
        Long parentId = parentCategory.getId();
        String dirBasePath = parentId.equals(0L) ? "#" : parentCategory.getDirPath();
        Integer dirLvl = parentId.equals(0L) ? 1 : parentCategory.getDirLvl() + 1;

        EamCategory category = new EamCategory();
        category.setId(ESUtil.getUUID());
        category.setDirName(createVo.getLabel());
        category.setParentId(parentId);
        category.setDataStatus(1);
        category.setCiCode(createVo.getCiCode());
        category.setDomainId(DEFAULT_DOMAIN_ID);
        category.setType(CategoryTypeEnum.SYSTEM.val());
        category.setDirPath(dirBasePath + category.getId() + "#");
        category.setDirLvl(dirLvl);
        category.setOwnerCode(owner);
        category.setCreator(DEFAULT_CREATOR);
        category.setModifier(DEFAULT_CREATOR);
        insertCategories.add(category);
    }

    /**
     * 资产库系统文件夹自动创建
     * @param context
     */
    private void designSysCategoryAutoCreate(CyAutoCreateContext context) {
        //资产库已有系统文件夹
        List<EamCategory> designSysCategories = context.getDesignSysCategories();
        //按资产库分组
        Map<Long, List<EamCategory>> groupByRootId = designSysCategories
                .stream().collect(Collectors.groupingBy(EamCategory::getRootParentId));
        //资产库id对应的ciCodes-category
        Map<Long, Map<String, EamCategory>> rootWithCiCodes = new ConcurrentHashMap<>();
        for (Map.Entry<Long, List<EamCategory>> entry : groupByRootId.entrySet()) {
            List<EamCategory> categories = entry.getValue();
            rootWithCiCodes.put(entry.getKey(), categories.stream().collect(
                    Collectors.toMap(EamCategory::getCiCode, e -> e, (k1,k2) -> k2)));
        }
        //创建应用系统&产品资产库没有的系统文件夹
        List<EamCategory> insertCategories = new ArrayList<>();
        //若应用系统or产品名称发生变更，更新系统文件夹名称
        List<EamCategory> updateCategories = new ArrayList<>();
        for (CyAutoCreateDTO autoCreateVo : context.getAutoCreateList()) {
            Long designRootId = context.getDesignRootId();
            Map<String, EamCategory> exists = rootWithCiCodes.getOrDefault(designRootId, new ConcurrentHashMap<>());
            EamCategory existCategory = exists.get(autoCreateVo.getCiCode());
            if (existCategory != null) {
                updateCategoryIfNeed(existCategory, autoCreateVo, updateCategories);
                continue;
            }
            EamCategory designRootCategory = getDesignRoot(designRootId);
            // 需要归档的资产 判断资产年份是否是当前年份 如果不是 则创建的系统文件夹自动归档至对应年份的文件夹下
            if (context.getArchiveFlag()) {
                if (autoCreateVo.getCreateTime() == null) {
                    log.info("ci资产创建时间为空，无法根据年份自动归档，ciCode:{}", autoCreateVo.getCiCode());
                }
                String year = String.valueOf(autoCreateVo.getCreateTime()).substring(0, 4);
                String currentYear = String.valueOf(LocalDate.now().getYear());
                if (!currentYear.equals(year)) {
                    log.info("资产创建年份不是当前年份，需要自动归档至对应年份，ciCode : {}", autoCreateVo.getCiCode());
                    // 获取/创建归档文件夹
                    designRootCategory = getCategoryByYear(year, designRootCategory,context);
                }
            }
            addSysCategory(autoCreateVo, DEFAULT_CREATOR, designRootCategory, insertCategories);
        }

        if (!CollectionUtils.isEmpty(insertCategories)) {
            //保存目录
            categoryDesignDao.saveOrUpdateBatch(insertCategories);
            //创建目录权限
            saveFolderPermissions(insertCategories, context);
        }
        if (!CollectionUtils.isEmpty(updateCategories)) {
            categoryDesignDao.saveOrUpdateBatch(updateCategories);
        }
    }

    private EamCategory getDesignRoot(Long designRootId) {
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.ROOT.val()));
        query.must(QueryBuilders.termQuery("dataStatus", 1));
        query.must(QueryBuilders.termQuery("id", designRootId));
        List<EamCategory> roots = categoryDesignDao.getListByQuery(query);
        return CollectionUtils.isEmpty(roots) ? null : roots.get(0);
    }

    private void saveFolderPermissions(List<EamCategory> insertCategories, CyAutoCreateContext context) {
        List<SysRole> sysRoles = getRoles(context);
        if (CollectionUtils.isEmpty(sysRoles)) {
            return;
        }
        FolderPermissionManager folderPermissionPrototype = initFolderPermissionPrototype();
        List<FolderPermissionManager> folderPermissions = new ArrayList<>();
        for (SysRole sysRole : sysRoles) {
            for (EamCategory category : insertCategories) {
                FolderPermissionManager folderPermission = null;
                try {
                    folderPermission = (FolderPermissionManager)folderPermissionPrototype.clone();
                } catch (CloneNotSupportedException e) {
                    throw new BinaryException(e);
                }
                folderPermission.setId(ESUtil.getUUID());
                folderPermission.setDirId(category.getId());
                folderPermission.setRoleId(sysRole.getId());
                folderPermission.setRoleName(sysRole.getRoleName());
                folderPermissions.add(folderPermission);
            }
        }
        if (!CollectionUtils.isEmpty(folderPermissions)) {
            folderPermissionManagerService.saveFolderPermissions(folderPermissions);
        }
    }

    private List<SysRole> getRoles(CyAutoCreateContext context) {
        List<String> designFolderAuthRoleNames = context.getDesignFolderAuthRoleNames();
        if (CollectionUtils.isEmpty(designFolderAuthRoleNames)) {
            log.info("分类{}暂未配置资产目录权限默认角色，不维护目录权限", context.getCategoryCreateClassCode());
            return new ArrayList<>();
        }
        //获取"员工"角色
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termsQuery("roleName.keyword", designFolderAuthRoleNames));
        List<SysRole> sysRoles = roleApiSvc.getRolesByQuery(query);
        if (CollectionUtils.isEmpty(sysRoles)) {
            log.info("分类{}配置资产目录权限默认角色{}未找到，不维护目录权限", context.getCategoryCreateClassCode(), designFolderAuthRoleNames);
            return new ArrayList<>();
        }
        return sysRoles;
    }

    /**
     * 初始化目录权限配置
     * @return
     */
    private FolderPermissionManager initFolderPermissionPrototype() {
        FolderPermissionManager folderPermissionPrototype = new FolderPermissionManager();
        FilePermission filePermission = new FilePermission();
        filePermission.setDelete(false);
        filePermission.setMove(false);
        filePermission.setRead(true);
        folderPermissionPrototype.setFilePermissions(filePermission);
        FolderApplicationScope folderApplicationScope = new FolderApplicationScope();
        folderApplicationScope.setChildFolder(true);
        folderApplicationScope.setCurrentFolder(true);
        folderApplicationScope.setChildFolderAndFile(true);
        folderPermissionPrototype.setFolderApplicationScope(folderApplicationScope);
        FolderPermission folderPermissions = new FolderPermission();
        folderPermissions.setCreate(false);
        folderPermissions.setDelete(false);
        folderPermissions.setMove(false);
        folderPermissions.setRename(false);
        folderPermissions.setRead(true);
        folderPermissionPrototype.setFolderPermissions(folderPermissions);
        folderPermissionPrototype.setModelPermission(new ModelPermission());
        return folderPermissionPrototype;
    }

    @Override
    public void categoryCreate(CyCreateReq req) {
        Assert.notNull(req, "ciCode不能为空");
        Assert.notNull(req.getCiCode(), "ciCode不能为空");
        Assert.notNull(req.getParentId(), "父目录id不能为空");
        EamCategory parentCategory = null;
        if (req.getParentId() == 0) {
            parentCategory = new EamCategory();
            parentCategory.setId(0L);
        } else {
            parentCategory = eamCategorySvc.getById(req.getParentId(), LibType.PRIVATE);
        }
        Assert.notNull(parentCategory, "父目录不存在");
        AXEAConf axeaConf = axeaConfService.getCategoryAutoConf();
        CcCiInfo ciInfo = getCIByCiCode(req.getCiCode());
        Assert.notNull(ciInfo, "未找到当前资产实例");
        String ciClassCode = ciInfo.getCiClass().getClassCode();
        if (!axeaConf.getClassConf().containsKey(ciClassCode)) {
            throw new BinaryException("当前资产实例暂不支持创建系统文件夹");
        }
        String label = getLabel(ciInfo.getCi());
        if (StringUtils.isBlank(label)) {
            log.error("当前资产实例暂不支持创建系统文件夹：无label");
            throw new BinaryException("当前资产实例暂不支持创建系统文件夹");
        }
        AXEAClassDetailConf classDetailConf = axeaConf.getClassConf().get(ciClassCode);
        List<String> ownerDefs = classDetailConf.getOwnerDefs();
        List<String> owners = new ArrayList<>();
        for (String ownerDef : ownerDefs) {
            String owner = ciInfo.getAttrs().get(ownerDef);
            List<String> loginCodes = this.analysisLoginCode(owner);
            if (!CollectionUtils.isEmpty(loginCodes)) {
                owners.addAll(loginCodes);
            }
        }
        String curLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //当前用户是否为该资产相关负责人
        if (!owners.contains(curLoginCode)) {
            UserInfo director = getDirector(ciInfo.getCi().getOwnerCode());
            //您当前没有【资产label】系统文件夹创建权限，若需创建【资产label】系统文件夹，请联系【资产创建人】进行权限添加
            String msg = "您当前没有\"".concat(label)
                    .concat("\"系统文件夹创建权限，若需创建\"")
                    .concat(label)
                    .concat("\"系统文件夹，请联系")
                    .concat(director.getUserName())
                    .concat("进行权限添加");
            throw new BinaryException(msg);
        }
        //当前用户"我的空间"内是否存在同ciCode的系统文件夹
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.SYSTEM.val()));
        query.must(QueryBuilders.termQuery("ownerCode.keyword", curLoginCode));
        query.must(QueryBuilders.termQuery("ciCode.keyword", req.getCiCode()));
        List<EamCategory> privateSysCategories = categoryPrivateDao.getListByQuery(query);
        if (!CollectionUtils.isEmpty(privateSysCategories)) {
            EamCategory privateSysCategory = privateSysCategories.get(0);
            if (privateSysCategory.getDataStatus() != null && privateSysCategory.getDataStatus() == 0) {
                throw new BinaryException("该系统文件夹已删除至回收站，若需使用，请于回收站还原该文件夹");
            }
            throw new BinaryException("我的空间内已存在该系统文件夹");
        }
        //创建系统文件夹
        List<EamCategory> insertCategories = new ArrayList<>();
        CyAutoCreateDTO createVo = new CyAutoCreateDTO();
        createVo.setLabel(label);
        createVo.setCiCode(req.getCiCode());
        addSysCategory(createVo, curLoginCode, parentCategory, insertCategories);
        if (!CollectionUtils.isEmpty(insertCategories)) {
            categoryPrivateDao.saveOrUpdateBatch(insertCategories);
        }
    }

    private UserInfo getDirector(String owner) {
        List<UserInfo> admins = getAdmins();
        if (StringUtils.isBlank(owner)) {
            return admins.get(0);
        }
        CSysUser cdt = new CSysUser();
        cdt.setLoginCodeEqual(owner);
        List<UserInfo> users = userSvc.getUserInfoByCdt(cdt, true);
        return CollectionUtils.isEmpty(users) ? admins.get(0) : users.get(0);
    }

    private List<UserInfo> getAdmins() {
        List<SysRole> roles = roleApiSvc.getRolesByQuery(QueryBuilders.termQuery("roleName.keyword", "admin"));
        if(org.apache.commons.collections4.CollectionUtils.isEmpty(roles)){
            return Collections.emptyList();
        }
        Long roleId = roles.get(0).getId();
        UserSearchBeanExtend beanExtend = new UserSearchBeanExtend();
        beanExtend.setPageNum(1);
        //审批人暂定200
        beanExtend.setPageSize(200);
        beanExtend.setRoleId(roleId);
        beanExtend.setOnlyCurrentOrg(true);
        Page<UserInfo> userInfoPage = userSvc.getListByQuery(beanExtend);
        if(CollectionUtils.isEmpty(userInfoPage.getData())){
            return Collections.emptyList();
        }
        return userInfoPage.getData();
    }

    @Override
    public CheckAssertDirRes checkAssertDir(CheckAssertDirReq req) {
        Assert.notNull(req, "资源类型不能为空");
        Assert.notNull(req.getAssetType(), "资源类型不能为空");
        if (!AssetType.DIAGRAM.equals(req.getAssetType())
                && !AssetType.SCHEME.equals(req.getAssetType())) {
            throw new BinaryException("不支持的资源类型");
        }
        Assert.notNull(req.getCiCode(), "资产ciCode不能为空");
        if (AssetType.DIAGRAM.equals(req.getAssetType())) {
            Assert.notNull(req.getArtifactId(), "制品id不能为空");
        }
        AXEAConf axeaConf = axeaConfService.getCategoryAutoConf();
        CcCiInfo ciInfo = getCIByCiCode(req.getCiCode());
        Assert.notNull(ciInfo, "未找到当前资产实例");
        String ciClassCode = ciInfo.getCiClass().getClassCode();
        if (!axeaConf.getClassConf().containsKey(ciClassCode)) {
            log.info("当前资产实例对应视图/方案暂不支持放在系统系统文件夹");
            return new CheckAssertDirRes(true, "success");
        }
        //非系统，不校验权限
        AXEAClassMapping systemClassMapping = axeaConfService.getSystemClassMapping();
        if (!ciClassCode.equals(systemClassMapping.getSystemClassCode())) {
            log.info("非系统，不校验权限");
            return new CheckAssertDirRes(true, "success");
        }

        String label = getLabel(ciInfo.getCi());
        if (StringUtils.isBlank(label)) {
            label = req.getCiCode();
        }
        AXEAClassDetailConf classDetailConf = axeaConf.getClassConf().get(ciClassCode);
        List<String> ownerDefs = classDetailConf.getOwnerDefs();
        List<String> owners = new ArrayList<>();
        for (String ownerDef : ownerDefs) {
            String owner = ciInfo.getAttrs().get(ownerDef);
            List<String> loginCodes = this.analysisLoginCode(owner);
            if (!CollectionUtils.isEmpty(loginCodes)) {
                owners.addAll(loginCodes);
            }
        }
        String curLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //当前用户是否为该资产相关负责人
        if (owners.contains(curLoginCode)) {
            return new CheckAssertDirRes(true, "success");
        }
        UserInfo director = getDirector(ciInfo.getCi().getOwnerCode());
        //视图：您当前没有【资产label】相关架构图绘制权限，若需创建【资产label】【制品类型名称】，请联系【资产创建人】进行权限添加
        if (AssetType.DIAGRAM.equals(req.getAssetType())) {
            EamArtifactVo artifactVo = artifactSvc.queryArtifact(req.getArtifactId());
            Assert.notNull(artifactVo, "未找到当前制品，请刷新列表重试");
            String msg = "您当前没有\"".concat(label)
                    .concat("\"相关架构图绘制权限，若需创建\"")
                    .concat(label)
                    .concat("\"")
                    .concat(artifactVo.getArtifactName())
                    .concat("请联系")
                    .concat(director.getUserName())
                    .concat("进行权限添加");
            return new CheckAssertDirRes(false, msg);
        }
        //方案：您当前没有【资产label】相关架构方案创建权限，若需创建，请联系【资产创建人】进行权限添加
        String msg = "您当前没有\"".concat(label)
                .concat("\"相关架构方案创建权限，若需创建，请联系")
                .concat(director.getUserName())
                .concat("进行权限添加");
        return new CheckAssertDirRes(false, msg);
    }

    @Override
    public Long getAssertDir(String ciCode) {
        if (StringUtils.isBlank(ciCode)) {
            throw new BinaryException("ciCode不能为空");
        }
        EamCategory category = eamCategorySvc.getByCiCode(ciCode, SysUtil.getCurrentUserInfo().getLoginCode(), LibType.PRIVATE);
        return category == null ? null : category.getId();
    }

    @Override
    public Map<String, Object> getAssertDirInfo(String ciCode) {
        if (StringUtils.isBlank(ciCode)) {
            throw new BinaryException("ciCode不能为空");
        }
        Map<String, Object> dirMap = null;
        EamCategory category = eamCategorySvc.getByCiCode(ciCode, SysUtil.getCurrentUserInfo().getLoginCode(), LibType.PRIVATE);
        if (!BinaryUtils.isEmpty(category)) {
            String dirName = getDirName(category);
            dirMap = new HashMap<>();
            dirMap.put("dirId", category.getId());
            dirMap.put("dirName", dirName);
        } else {
            log.info("当前用户[{}]我的空间下不存在绑定资产[ciCode:{}]对应的系统文件夹", SysUtil.getCurrentUserInfo().getLoginCode(), ciCode);
        }
        return dirMap;
    }

    @Override
    public AssertCheckoutRes assertCheckoutInfo(AssertCheckoutReq req) {
        log.info("视图或方案检出信息入参:{}", JSON.toJSONString(req));
        Assert.notNull(req, "资源类型不能为空");
        Assert.notNull(req.getAssetType(), "资源类型不能为空");
        Assert.notNull(req.getCheckoutType(), "操作类型不能为空");
        if (!AssetType.DIAGRAM.equals(req.getAssetType())
                && !AssetType.SCHEME.equals(req.getAssetType())) {
            throw new BinaryException("不支持的资源类型");
        }
        if (AssetType.DIAGRAM.equals(req.getAssetType())) {
            Assert.notNull(req.getDiagramId(), "视图id不能为空");
        }
        if (AssetType.SCHEME.equals(req.getAssetType())) {
            Assert.notNull(req.getPlanId(), "方案id不能为空");
            if (CheckoutType.CHECKOUT.equals(req.getCheckoutType())) {
                throw new BinaryException("方案暂不支持检出");
            }
        }
        checkAssertCheckoutAuth(req);
        AssertCheckoutRes res;
        switch (req.getCheckoutType()) {
            case CHECKOUT:
                Map<String, Object> result = generalPullSvc.existRelateData(req.getDiagramId(), null);
                res = JSON.parseObject(JSON.toJSONString(result), AssertCheckoutRes.class);
                if (result.get("isExist") != null) {
                    res.setDirEdit(!Boolean.valueOf(result.get("isExist").toString()));
                }
                //非视图检出
                if (res.getCheckType() != null && res.getCheckType() != 1) {
                    log.info("非视图检出");
                    return res;
                }
                //设计空间有原图
                if (Boolean.FALSE.equals(res.getDirEdit())) {
                    log.info("设计空间有原图");
                    return res;
                }
                setDiagramSysDirIfNeed(res, req.getDiagramId());
                return res;
            case CHECKOUT_SAVE:
                switch (req.getAssetType()) {
                    case DIAGRAM:
                        res = new AssertCheckoutRes();
                        res.setCheckType(1);
                        res.setDirEdit(true);
                        res.setCheckInfo(new AssertCheckoutRes.CheckInfo());
                        setDiagramSysDirIfNeed(res, req.getDiagramId());
                        return res;
                    case SCHEME:
                        res = new AssertCheckoutRes();
                        res.setDirEdit(true);
                        res.setCheckInfo(new AssertCheckoutRes.CheckInfo());
                        setPlanSysDirIfNeed(res, req.getPlanId());
                        return res;
                    default:
                        throw new BinaryException("不支持的资源类型");
                }
            default:
                throw new BinaryException("不支持的操作类型");
        }
    }

    private void setDiagramSysDirIfNeed(AssertCheckoutRes res, String diagramId) {
        //有无绑定资产
        EamDiagramRelationSys diagramRelationSys = diagramRelationSysService.getEamDiagramRelationSys(diagramId);
        if (diagramRelationSys == null) {
            log.info("当前待检出/检出另存为视图无绑定资产");
            return;
        }
        //绑定资产信息
        CcCiInfo bindCIInfo = getCIByCiCode(diagramRelationSys.getEsSysId());
        if (bindCIInfo == null) {
            log.info("未查到当前待检出/检出另存为视图的绑定资产");
            return;
        }
        //绑定资产分类信息
        CCcCiClass cdt = new CCcCiClass();
        cdt.setId(bindCIInfo.getCi().getClassId());
        List<CcCiClassInfo> classInfos = ciClassSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(classInfos)) {
            log.info("未查到绑定资产所属分类信息");
            return;
        }
        CcCiClassInfo ciClassInfo = classInfos.get(0);
        //有系统文件夹分类配置信息
        AXEAConf axeaConf = axeaConfService.getCategoryAutoConf();
        if (!axeaConf.getClassConf().containsKey(ciClassInfo.getCiClass().getClassCode())) {
            log.info("绑定资产所属分类不在有系统文件夹分类配置信息内");
            return;
        }
        //当前人我的空间下绑定资产的系统文件夹
        String curUsrLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        EamCategory category = eamCategorySvc.getByCiCode(diagramRelationSys.getEsSysId(), curUsrLoginCode, LibType.PRIVATE);
        if (category == null) {
            log.info("当前用户[{}]我的空间下不存在绑定资产[ciCode:{}]对应的系统文件夹", curUsrLoginCode, diagramRelationSys.getEsSysId());
            return;
        }
        String dirName = getDirName(category);
        res.setDirEdit(false);
        AssertCheckoutRes.CheckInfo checkInfo = res.getCheckInfo();
        checkInfo.setDirId(category.getId());
        checkInfo.setDirName(dirName);
        res.setCheckInfo(checkInfo);
    }

    private void setPlanSysDirIfNeed(AssertCheckoutRes res, Long planId) {
        PlanDesignInstance plan = planDesignInstanceService.getPlanDesignInstance(planId);
        if (plan == null || plan.getTemplateId() == null || StringUtils.isBlank(plan.getDefaultSystemCiCode())) {
            log.info("未找到方案/无方案模板信息/方案无绑定资产:{}", JSON.toJSONString(plan));
            return;
        }
        DeliverableTemplate deliverableTemplate = deliverableTemplateDao.getById(plan.getTemplateId());
        if (deliverableTemplate == null) {
            log.info("未找到方案模板信息[templateId:{}]", plan.getTemplateId());
            return;
        }
        //查绑定资产
        String defaultSystemCiCode = plan.getDefaultSystemCiCode();
        List<ESCIInfo> esciInfos = ciSwitchSvc.getCiByCodes(Arrays.asList(defaultSystemCiCode), null, LibType.DESIGN);
        if (CollectionUtils.isEmpty(esciInfos)) {
            log.info("未找到方案绑定资产[ciCode:{}]", defaultSystemCiCode);
            return;
        }
        ESCIInfo ciInfo = esciInfos.get(0);
        List<BindAssetVo> bindAssetList = deliverableTemplate.getBindAssetList();
        BindAssetVo bindAssetVo = bindAssetList.get(0);
        //绑定资产对应的分类和模板绑定资产分类不一致
        if (!bindAssetVo.getClassId().equals(ciInfo.getClassId())) {
            log.info("绑定资产对应的分类[ciClassId:{}]和模板绑定资产分类[bindClassId:{}]不一致", ciInfo.getClassId(), bindAssetVo.getClassId());
            return;
        }
        CCcCiClass cdt = new CCcCiClass();
        cdt.setId(bindAssetVo.getClassId());
        List<CcCiClassInfo> classInfos = ciClassSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(classInfos)) {
            log.info("未找到绑定资产所属分类:ciClassId:{}", bindAssetVo.getClassId());
            return;
        }
        CcCiClassInfo ciClassInfo = classInfos.get(0);
        AXEAConf axeaConf = axeaConfService.getCategoryAutoConf();
        //模板绑定分类非指定需要创建系统文件夹分类
        if (!axeaConf.getClassConf().containsKey(ciClassInfo.getCiClass().getClassCode())) {
            log.info("模板绑定分类[classCode:{}]非指定需要创建系统文件夹分类[{}]",
                    ciClassInfo.getCiClass().getClassCode(), JSON.toJSONString(axeaConf.getClassConf()));
            return;
        }
        //当前人我的空间下绑定资产的系统文件夹
        String curUsrLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        EamCategory category = eamCategorySvc.getByCiCode(defaultSystemCiCode, curUsrLoginCode, LibType.PRIVATE);
        if (category == null) {
            log.info("当前用户[{}]我的空间下不存在绑定资产[ciCode:{}]对应的系统文件夹", curUsrLoginCode, defaultSystemCiCode);
            return;
        }
        //有绑定资产对应的系统文件夹
        String dirName = getDirName(category);
        res.setDirEdit(false);
        AssertCheckoutRes.CheckInfo checkInfo = res.getCheckInfo();
        checkInfo.setDirId(category.getId());
        checkInfo.setDirName(dirName);
        res.setCheckInfo(checkInfo);
        AssertCheckoutRes.Template template = new AssertCheckoutRes.Template();
        template.setId(deliverableTemplate.getId());
        template.setTemplateName(deliverableTemplate.getTemplateName());
        template.setBindAssetList(bindAssetList);
        res.setTemplate(template);
        Map<String, String> asset = MapUtil.newHashMap(2);
        asset.put("ciCode", ciInfo.getCiCode());
        asset.put("name", getSysName(ciInfo));
        res.setAsset(asset);
    }

    private String getDirName(EamCategory category) {
        if (BinaryUtils.isEmpty(category)) {
            return null;
        }
        String dirPath = category.getDirPath();
        String[] dirIds = dirPath.split("#");
        List<String> idsStrs = Arrays.asList(dirIds);
        List<Long> ids = new ArrayList<>();
        for (String idStr : idsStrs) {
            if (!BinaryUtils.isEmpty(idStr)) {
                ids.add(Long.valueOf(idStr));
            }
        }
        List<EamCategory> privateCategoryList = eamCategorySvc.getByIds(ids, LibType.PRIVATE);
        privateCategoryList = privateCategoryList.stream().sorted(Comparator.comparing(EamCategory::getDirLvl)).collect(Collectors.toList());
        String dirName = "我的空间";
        for (EamCategory privateCategory : privateCategoryList) {
            dirName = dirName + "/" + privateCategory.getDirName();
        }
        return dirName;
    }

    private String getSysName(CcCi ci) {
        String ciLabel = ci.getCiLabel();
        List<String> datas = JSONObject.parseArray(ciLabel, String.class);
        if (!BinaryUtils.isEmpty(ciLabel) && !CollectionUtils.isEmpty(datas)) {
            String[] strings1 = datas.toArray(new String[0]);
            return StringUtils.join(strings1, ",");
        }

        return formatSysName(ci.getCiPrimaryKey());
    }

    private String formatSysName(String ciPrimaryKey) {
        List<String> strings = JSONObject.parseArray(ciPrimaryKey, String.class);
        if (!CollectionUtils.isEmpty(strings)) {
            strings.remove(0);
            String[] strings1 = strings.toArray(new String[0]);
            return StringUtils.join(strings1, ",");
        }
        return null;
    }

    private void checkAssertCheckoutAuth(AssertCheckoutReq req) {
        AXEAConf axeaConf = axeaConfService.getCategoryAutoConf();
        String ciCode = "";
        switch (req.getAssetType()) {
            case DIAGRAM:
                EamDiagramRelationSys diagramRelationSys = diagramRelationSysService.getEamDiagramRelationSys(req.getDiagramId());
                if (diagramRelationSys == null) {
                    return;
                }
                ciCode = diagramRelationSys.getEsSysId();
                break;
            case SCHEME:
                PlanDesignInstance plan = planDesignInstanceService.getPlanDesignInstance(req.getPlanId());
                if (plan == null || StringUtils.isBlank(plan.getDefaultSystemCiCode())) {
                    return;
                }
                ciCode = plan.getDefaultSystemCiCode();
                break;
            default:
                throw new BinaryException("不支持的资源类型");
        }
        if (StringUtils.isBlank(ciCode)) {
            return;
        }
        CcCiInfo ciInfo = getCIByCiCode(ciCode);
        if (ciInfo == null) {
            return;
        }
        String ciClassCode = ciInfo.getCiClass().getClassCode();
        if (!axeaConf.getClassConf().containsKey(ciClassCode)) {
            return;
        }
        String label = getLabel(ciInfo.getCi());
        if (StringUtils.isBlank(label)) {
            label = ciCode;
        }
        AXEAClassDetailConf classDetailConf = axeaConf.getClassConf().get(ciClassCode);
        List<String> ownerDefs = classDetailConf.getOwnerDefs();
        List<String> owners = new ArrayList<>();
        for (String ownerDef : ownerDefs) {
            String owner = ciInfo.getAttrs().get(ownerDef);
            List<String> loginCodes = this.analysisLoginCode(owner);
            if (!CollectionUtils.isEmpty(loginCodes)) {
                owners.addAll(loginCodes);
            }
        }
        String curLoginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //当前用户是否为该资产相关负责人
        if (owners.contains(curLoginCode)) {
            return;
        }
        UserInfo director = getDirector(ciInfo.getCi().getOwnerCode());
        //您当前没有【资产label】相关资产检出权限，若需检出，请联系【资产创建人】进行权限添加
        String msg = "您当前没有\"".concat(label)
                .concat("\"相关资产检出权限，若需检出，请联系")
                .concat(director.getUserName())
                .concat("进行权限添加");
        throw new BinaryException(msg);
    }

    @Override
    public void archiveCategory(String year) {
        AXEAConf conf = axeaConfService.getCategoryAutoConf();
        String projectClassCode = conf.getClassMapping().getProjectClassCode();
        AXEAClassDetailConf classDetailConf = conf.getClassConf().get(projectClassCode);

        // 根据年份查询有没有项目资产（已创建系统文件夹的）在该年份创建
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.SYSTEM.val()));
        query.must(QueryBuilders.termQuery("dataStatus", 1));
        query.must(QueryBuilders.termQuery("parentId", classDetailConf.getDesignRootId()));
        List<EamCategory> systemCategories = categoryDesignDao.getListByQueryScroll(query);
        if (BinaryUtils.isEmpty(systemCategories)) {
            log.info("没有待归档的系统文件夹");
            return;
        }
        List<String> ciCodes = systemCategories.stream().map(EamCategory::getCiCode).collect(Collectors.toList());
        Map<String, EamCategory> categoryMap = systemCategories.stream().collect(Collectors.toMap(EamCategory::getCiCode, each -> each, (k1, k2) -> k1));
        // 查询在该年份的项目资产
        List<CcCiInfo> ciInfos = getCIByYear(projectClassCode, ciCodes, year);
        if (BinaryUtils.isEmpty(ciInfos)) {
            log.info("没有符合归档条件的项目资产");
            return;
        }

        EamCategory designRootCategory = getDesignRoot(classDetailConf.getDesignRootId());
        if (BinaryUtils.isEmpty(designRootCategory)) {
            log.info("designRootCategory 为空");
            return;
        }

        // 获取/创建归档文件夹
        EamCategory archiveCategory = getCategoryByYear(year, designRootCategory, conf);

        // 归档项目资产对应的系统文件夹
        List<EamCategory> categoryList = new ArrayList<>();
        for (CcCiInfo ciInfo : ciInfos) {
            String ciCode = ciInfo.getCi().getCiCode();
            EamCategory category = categoryMap.get(ciCode);
            category.setDirLvl(archiveCategory.getDirLvl() + 1);
            category.setDirPath(archiveCategory.getDirPath() + category.getId()  + "#");
            category.setParentId(archiveCategory.getId());
            categoryList.add(category);
        }

        // 保存归档后的系统文件夹
        categoryDesignDao.saveOrUpdateBatch(categoryList);
    }

    private EamCategory getCategoryByYear(String year, EamCategory designRootCategory,AXEAConf conf) {
        String projectClassCode = conf.getClassMapping().getProjectClassCode();
        AXEAClassDetailConf classDetailConf = conf.getClassConf().get(projectClassCode);
        CyAutoCreateContext context = CyAutoCreateContext.builder()
                .categoryCreateClassCode(projectClassCode)
                .designFolderAuthRoleNames(classDetailConf.getDesignFolderAuthRoleNames())
                .build();
        return getCategoryByYear(year, designRootCategory, context);
    }

    private EamCategory getCategoryByYear(String year, EamCategory designRootCategory,CyAutoCreateContext context) {
        BoolQueryBuilder query;
        String dirName = "存档-" + year;
        // 查询该年份是否已经创建了归档文件夹
        query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("type", CategoryTypeEnum.ARCHIVE.val()));
        query.must(QueryBuilders.termQuery("dataStatus", 1));
        query.must(QueryBuilders.termQuery("dirName", dirName));
        List<EamCategory> archiveCategories = categoryDesignDao.getListByQuery(query);
        EamCategory archiveCategory; // 归档文件夹

        if (BinaryUtils.isEmpty(archiveCategories)) {
            Long parentId = designRootCategory.getId();
            String dirBasePath = parentId.equals(0L) ? "#" : designRootCategory.getDirPath();
            Integer dirLvl = parentId.equals(0L) ? 1 : designRootCategory.getDirLvl() + 1;
            // 没有归档文件夹 则创建一个
            archiveCategory = new EamCategory();
            archiveCategory.setId(ESUtil.getUUID());
            archiveCategory.setDirName(dirName);
            archiveCategory.setParentId(parentId);
            archiveCategory.setDataStatus(1);
            archiveCategory.setDomainId(DEFAULT_DOMAIN_ID);
            archiveCategory.setType(CategoryTypeEnum.ARCHIVE.val());
            archiveCategory.setDirPath(dirBasePath + archiveCategory.getId() + "#");
            archiveCategory.setDirLvl(dirLvl);
            archiveCategory.setOwnerCode(DEFAULT_CREATOR);
            archiveCategory.setCreator(DEFAULT_CREATOR);
            archiveCategory.setModifier(DEFAULT_CREATOR);
            categoryDesignDao.saveOrUpdate(archiveCategory);
            List<EamCategory> eamCategories = Collections.singletonList(archiveCategory);
            saveFolderPermissions(eamCategories, context);
        } else {
            archiveCategory = archiveCategories.get(0);
        }
        return archiveCategory;
    }
}
